//
// Created by 王乐 on 2022/2/5.
//

#include "LeCameraFlyBehaviour.h"

#include "glm/gtc/matrix_transform.hpp"

#include "LeInput.h"
#include "LeTime.h"
#include "LeScreenSettings.h"

#include "LeTransform.h"

LeCameraFlyBehaviour::LeCameraFlyBehaviour()
{}

LeCameraFlyBehaviour::~LeCameraFlyBehaviour()
{

}

void LeCameraFlyBehaviour::Awake()
{
    yaw = -90.f;
    pitch = 0.0f;
    moveSpeed = 2.5f;
    mouseSensitivity = 0.1f;
    zoom = 45.0f;

    Front = glm::vec3(0.0f, 0.0f, -1.0f);
    WorldUp = glm::vec3(0.0f, 1.0f, 0.0f);
    updateCameraVectors();

    firstMouse = true;
    dLastX= SCR_WIDTH / 2.0f;
    dLastY= SCR_HEIGHT / 2.0f;
    bIsPerspectiveView = true;
}

glm::mat4 LeCameraFlyBehaviour::getViewMatrix()
{
    return glm::lookAt(m_pTransform->m_Position,
                       m_pTransform->m_Position + Front,
                       Up);
}

glm::mat4 LeCameraFlyBehaviour::getProjectionMatrix()
{
    if(bIsPerspectiveView)
        return glm::perspective(glm::radians(zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
    else
        return glm::ortho(-2.0f*zoom*mouseSensitivity, 2.0f*zoom*mouseSensitivity,
                          -3.0f*zoom*mouseSensitivity, 3.0f*zoom*mouseSensitivity,
                          0.1f*zoom*mouseSensitivity, 15.0f*zoom*mouseSensitivity);
}

void LeCameraFlyBehaviour::Start()
{

}

void LeCameraFlyBehaviour::Update()
{
    //Process Keyboard input
    //----------------------
    if (LeInput::getKey(GLFW_KEY_W) == GLFW_PRESS)
        processKeyboard(CM_FORWARD);
    if (LeInput::getKey(GLFW_KEY_S) == GLFW_PRESS)
        processKeyboard(CM_BACKWARD);
    if (LeInput::getKey(GLFW_KEY_A) == GLFW_PRESS)
        processKeyboard(CM_LEFT);
    if (LeInput::getKey(GLFW_KEY_D) == GLFW_PRESS)
        processKeyboard(CM_RIGHT);

    //Process Mouse movement
    //----------------------
    if (firstMouse)
    {
        dLastX = LeInput::getMouseX();
        dLastY = LeInput::getMouseY();
        firstMouse = false;
    }
    float xoffset = LeInput::getMouseX() - dLastX;
    float yoffset = dLastY - LeInput::getMouseY(); // reversed since y-coordinates go from bottom to top
    dLastX = LeInput::getMouseX();
    dLastY = LeInput::getMouseY();
    processMouseMovement(xoffset, yoffset, true);

    //Process Mouse scroll
    //--------------------
    processMouseScroll(LeInput::getScrollYOffset());
}

void LeCameraFlyBehaviour::updateCameraVectors()
{
    // calculate the new Front vector
    glm::vec3 front;
    front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
    front.y = sin(glm::radians(pitch));
    front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
    Front = glm::normalize(front);
    // also re-calculate the Right and Up vector
    Right = glm::normalize(glm::cross(Front, WorldUp));  // normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
    Up    = glm::normalize(glm::cross(Right, Front));
}

void LeCameraFlyBehaviour::processKeyboard(Camera_Movement direction)
{
    float velocity = moveSpeed * LeTime::dDeltaTime;
    if (direction == CM_FORWARD)
         m_pTransform->m_Position += Front * velocity;
    if (direction == CM_BACKWARD)
        m_pTransform->m_Position -= Front * velocity;
    if (direction == CM_LEFT)
        m_pTransform->m_Position -= Right * velocity;
    if (direction == CM_RIGHT)
        m_pTransform->m_Position += Right * velocity;
}

void LeCameraFlyBehaviour::processMouseMovement(float xoffset, float yoffset, bool constrainPitch)
{
    xoffset *= mouseSensitivity;
    yoffset *= mouseSensitivity;

    yaw   += xoffset;
    pitch += yoffset;

    // make sure that when pitch is out of bounds, screen doesn't get flipped
    if (constrainPitch)
    {
        if (pitch > 89.0f)
            pitch = 89.0f;
        if (pitch < -89.0f)
            pitch = -89.0f;
    }

    // update Front, Right and Up Vectors using the updated Euler angles
    updateCameraVectors();
}

void LeCameraFlyBehaviour::processMouseScroll(float yoffset)
{
    zoom -= (float)yoffset;
    if (zoom < 1.0f)
        zoom = 1.0f;
    if (zoom > 45.0f)
        zoom = 45.0f;
}
